Android UI测试框架Espresso

前言

最近这两周工作相对不那么忙了,所以就抽空结合官方文档研究了一下Android UI测试框架Espresso,从框架的setup到测试用例编写到执行测试用例

简介

Espresso是Google官方推出的开源UI测试框架,它的特点就是简洁,很容易上手编写测试用例,而且关于UI测试Espresso支持的也越来越完善了
官方项目地址:点击查看Espresso测试框架详细信息

安装

在dependencies里面添加引用

1
2
3
4
5
6
7
8
// App dependencies
compile 'com.android.support:support-annotations:23.0.1'
// Testing-only dependencies
// Force usage of support annotations in the test app, since it is internally used by the runner module.
androidTestCompile 'com.android.support:support-annotations:23.0.1'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'

android.defaultConfig设置instrumentation runner

1
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

这里有一点需要注意的是官方文档中添加依赖的样例是这样的

1
2
3
4
5
6
7
8
dependencies {
// App\'s dependencies, including test
compile 'com.android.support:support-annotations:22.2.0'

// Testing-only dependencies
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

如果按照官方的这个来,build的时候你会遇到这样一个错误:

1
Error:Conflict with dependency 'com.android.support:support-annotations'

  • 详情如下图所示:

  • 产生这种错误的原因如下:
    通常是因为你的主程序和测试程序使用了相同的库,但是使用的版本却不一样,你可能会觉得奇怪,看代码里面测试依赖并没有引用support-annotations库啊,为什么会说测试程序和主程序使用了相同的库,这是因为在测试库runner中内部使用了support-annotations库,而且我们知道在运行的时候主程序apk和测试apk是共享同一个进程和相同的class path,这就代表主程序和测试程序必须使用相同版本的任何依赖库,一旦版本不同,所以才会出现上面的冲突错误

  • 形象说明见下图:

  • 解决办法:
    既然知道了问题产生的原因,那么解决办法也很简单,两种方法:

  1. 更改主程序里面对应冲突库的版本使其于测试程序里面的版本一致
  2. 更改测试程序里面冲突库的版本使其于主程序中的版本一致
    通常比较简单的做法就是在build.gradle文件中显式加入另一个test dependencies和主程序代码中的冲突库版本一致的库,这样就强制要求测试程序也使用和主程序相同版本的库文件,像这样:
    1
    2
    3
    4
    5
    6
    7
    8
    dependencies {
    // App\'s dependencies, including test
    compile '...:support-annotations:23.3.0'

    // Testing-only dependencies
    // Force the same version of support-annotations
    compile '...:support-annotations:23.3.0'
    }

编写测试用例

这个很简单,只要结合官方文档给出的例子,看看文档说明和对应的API,很容易上手,下面是我写的一个打开NavigationView的测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void openNavigationView() {
onView(withId(R.id.drawer_layout)).perform(
actionOpenNavigationView());
}

private ViewAction actionOpenDrawer() {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(DrawerLayout.class);
}
@Override
public String getDescription() {
return "open drawer";
}

@Override
public void perform(UiController uiController, View view) {
((DrawerLayout) view).openDrawer(GravityCompat.START);
}
};
}

解释几个关键类:

Espresso

和视图views的交互切入点(通过onView和onData),而且也会暴露不需要依赖任何view的API(eg.pressBack)

ViewMatchers

这个是我们如何find views的方式. ViewMatchers包含一个允许你在视图层次结构中找到指定的view的matchers的集合.以上,我们使用withId(R.id.etInput)来指明我们正在寻找的一个EditText,通过id = R.id.etInput

ViewActions

这是我们如何和views交互,我们使用typeText(…)方法来在EditText写入hello

##ViewAssertions
这是我们的验证类. 我们使用ViewAssertions来验证views的指定属性. 大部分时候你将会使用由ViewMatchers下驱动的ViewAssertions. 在我们上面的例子中withText(…)方法实际上返回一个我们已经使用matchs(…)方法转换成一个ViewAssertion的ViewMatcher

Espresso测试的标准模式是find a view(ViewMatchers), 在view上做一些事情(ViewActions), 然后验证view的一些属性(ViewAssertions).

例子:

1
2
3
onView(withId(R.id.my_view))      // withId(R.id.my_view) is a ViewMatcher
.perform(click()) // click() is a ViewAction
.check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

附上一张官网帮助记忆的小抄:

执行

执行测试用例有两种方法:

  1. 直接像运行app一样,通过Android Studio选择执行对应的测试程序
  2. 在项目根目录执行:./gradlew :testPreject:connectedAndroidTest

参考

1.https://google.github.io/android-testing-support-library/
2.https://guides.codepath.com/android/UI-Testing-with-Espresso
3.https://www.youtube.com/watch?v=OOEDKf06WqA